#include "prefilter.hpp"

#include "img.hpp"

#include <cmath>

void prefilter::equalize(image& img, const unsigned int nb_levels)
{
	img.equalize(nb_levels);
}

void prefilter::toGrayscale(image& img)
{
	img.toGrayscale();
}

void prefilter::toBlackAndWhite(image& img)
{
	img.quantize(2);
	img.normalize();
}

void prefilter::removeBackground(image& img, const unsigned char* color)
{
	const unsigned char default_color[4] = { 255, 255, 255, 0 };
	const size_t W = img.getWidth();
	const size_t H = img.getHeight();
	const size_t S = img.getSpectrum();
	const size_t A = W*H;
	unsigned char* data = img.getData();
	float* filter = new float[A];
	float w_txt = 0; // Color weight of text.
	float w_bg = 0; // Color weight of background.
	float w_max = 0;

	for (size_t i = 0; i < W; ++i)
		w_bg += sqrt(data[i] * data[i] + // Red.
		             (S > 2 ? data[A+i] * data[A+i] + // Green.
		              data[A+A+i] * data[A+A+i] : 0)); // Blue.
	w_bg /= (float)W; // = "Average amount of ink" used on background.

	// Text typically resides at the center of image, so...
	for (size_t i = (H >> 1)*W; i < (H >> 1) * W + W; ++i)
		w_txt += sqrt(data[i] * data[i] +
		              (S > 2 ? data[A+i] * data[A+i] +
		                       data[A+A+i] * data[A+A+i] : 0));
	w_txt /= (float)W;

	color = color ? color : default_color;
	for (size_t i = 0; i < A; ++i)
		filter[i] = 1.0f;

	for (size_t w = W; w > 1; w >>= 1)
		// +1 is needed in case W is odd at some point.
		// E.g. W= 5, x0= {0, 2, 4}, resulting out-of-bounds.
		for (size_t x0 = 0; x0 + 1 < W; x0 += w)
			for (size_t y = 0; y < H - 1; ++y)
			{
				const size_t j = y * W + x0;
				float w_x0 = 0;

				for (size_t i = j; i < j + w; ++i)
					w_x0 += sqrt(data[i]*data[i] +
					             (S > 2 ? data[A+i] *
					              data[A+i]+data[A+A+i] *
					              data[A+A+i] : 0));
				w_x0 /= (float)w;

				// Calc how much this range resembles text.
				float d_bg = fabs(w_bg - w_x0);
				float d_txt = fabs(w_txt - w_x0);
				w_x0 = d_bg / (d_bg + d_txt);

				for (size_t i = j; i < j + w; ++i)
				{
					filter[i] *= w_x0;
					w_max = w_max > filter[i] ? w_max :
					        filter[i];
				}
			}

	for (size_t i = 0; i < A; ++i)
	{
		float a = filter[i] = sqrt(filter[i] / w_max);
		data[i] = data[i] * a + color[0] * (1.0f - a);

		if (S > 2)
		{
			data[A+i] = data[A+i] * a + color[1] * (1.0f - a);
			data[A+A+i] = data[A+A+i]*a + color[2] * (1.0f-a);
		}
	}

	delete[] filter;
}

void prefilter::sharpen(image& img, const float amplitude)
{
	img.sharpen(amplitude);
}

